//
//  ZYMultipartFormDataStream.m
//  ZYWebImage
//
//  Created by wangzhipan on 2025/4/2.
//

#import "ZYMultipartFormDataStream.h"

@interface ZYMultipartFormDataStream() <NSStreamDelegate> {
    NSStreamStatus _status;
    NSError *_error;
    NSDictionary<NSString *, NSString *> * _params;
    NSString * _name;
    NSString * _fileName;
    NSURL * _fileURL;
    NSString * _mimeType;
    NSStringEncoding _stringEncoding;
}

@property (nonatomic, strong, nullable) NSArray<NSInputStream *> *streams;
@property (nonatomic, assign) NSUInteger currentStreamIndex;
@property (nonatomic, strong, nullable) NSInputStream *currentStream;
@property (nonatomic, copy, nullable) NSString *boundary;
@property (nonatomic, assign) unsigned long long contentLength;
@end

@implementation ZYMultipartFormDataStream

- (id)copyWithZone:(NSZone *)zone
{
    ZYMultipartFormDataStream *stream = [[ZYMultipartFormDataStream alloc] initWithBoundary:_boundary
                                                                                     params:_params
                                                                                       name:_name
                                                                                   fileName:_fileName
                                                                                    fileURL:_fileURL
                                                                                   mimeType:_mimeType
                                                                             stringEncoding:_stringEncoding];
    return stream;
}

- (instancetype)initWithBoundary:(NSString *)boundary
                          params:(NSDictionary<NSString *, NSString *> *)params
                            name:(NSString *)name
                        fileName:(NSString *)fileName
                         fileURL:(NSURL *)fileURL
                        mimeType:(NSString *)mimeType
                  stringEncoding:(NSStringEncoding)stringEncoding
{
    if (self = [super init]) {
        _boundary = boundary;
        _params = [params copy];
        _name = [name copy];
        _fileName = [fileName copy];
        _mimeType = [mimeType copy];
        _stringEncoding = stringEncoding;
        _status = NSStreamStatusNotOpen;
        _currentStreamIndex = 0;
        
        //构建所有sub stream
        NSMutableArray *streams = [NSMutableArray array];
        
        //1、先添加参数部分
        [params enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
            NSString *header = [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n", boundary, key];
            NSString *footer = @"\r\n";
            
            [streams addObject:[NSInputStream inputStreamWithData:[header dataUsingEncoding:stringEncoding]]];
            [streams addObject:[NSInputStream inputStreamWithData:[obj dataUsingEncoding:stringEncoding]]];
            [streams addObject:[NSInputStream inputStreamWithData:[footer dataUsingEncoding:stringEncoding]]];
            
            self.contentLength += [header lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
            self.contentLength += [obj lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
            self.contentLength += [footer lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
        }];
        
        // 2. 添加文件头
        NSString *fileHeader = [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\nContent-Type: %@\r\n\r\n",
                                boundary, name, fileName, mimeType];
        [streams addObject:[NSInputStream inputStreamWithData:[fileHeader dataUsingEncoding:NSUTF8StringEncoding]]];
        self.contentLength += [fileHeader lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
        
        // 3. 添加文件流
        NSInputStream *fileStream = [NSInputStream inputStreamWithURL:fileURL];
        if (fileStream) {
            [streams addObject:fileStream];
            
            NSNumber *fileSize;
            [fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:nil];
            self.contentLength += [fileSize unsignedLongLongValue];
        }
        
        // 4. 添加结束边界
        NSString *endBoundary = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary];
        [streams addObject:[NSInputStream inputStreamWithData:[endBoundary dataUsingEncoding:NSUTF8StringEncoding]]];
        self.contentLength += [endBoundary lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
        
        _streams = [streams copy];
    }
    return self;
}

#pragma mark - overWrite NSInputStream
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len
{
    if (_status == NSStreamStatusClosed || _status == NSStreamStatusError) {
        return -1;
    }
    NSUInteger totalReadLen = 0;
    
    while (totalReadLen < len) {
        if (!_currentStream) {
            if (_currentStreamIndex < _streams.count) {
                _currentStream = _streams[_currentStreamIndex];
                _currentStreamIndex++;
                [_currentStream open];
            }
            else {
                _status = NSStreamStatusAtEnd;
                break;
            }
        }
        
        /*
         - NSInputStream 内部维护了一个读取位置指针，每次读取后自动向前移动
         - 不需要手动管理读取位置，流会自动记录
         - 如果想要重新从头读取，需要关闭流并重新打开
         */
        NSUInteger readBytes = [_currentStream read:buffer + totalReadLen
                                          maxLength:len - totalReadLen];
        
        if (readBytes < 0) {
            _error = _currentStream.streamError;
            _status = NSStreamStatusError;
            return readBytes;
        } else if (readBytes == 0) {
            [_currentStream close];
            _currentStream = nil;
            continue;
        }
        totalReadLen += readBytes;
    }
    return totalReadLen;
}

- (BOOL)getBuffer:(uint8_t * _Nullable *)buffer length:(NSUInteger *)len
{
    return NO;
}

- (BOOL)hasBytesAvailable
{
    if (_status == NSStreamStatusAtEnd) {
        return NO;
    }

    if (_currentStream && [_currentStream hasBytesAvailable]) {
        return YES;
    }

    return _currentStreamIndex < _streams.count;
}

- (void)open 
{
    if (_status != NSStreamStatusNotOpen) {
        return;
    }
    _status = NSStreamStatusOpen;
}

- (void)close 
{
    if (_status == NSStreamStatusClosed) {
        return;
    }
    
    [_currentStream close];
    _currentStream = nil;
    _status = NSStreamStatusClosed;
}

- (NSStreamStatus)streamStatus 
{
    return _status;
}

- (NSError *)streamError 
{
    return _error;
}
@end
